/************************************************************
 * USER COMMON HEADER FILE
 ************************************************************/

#ifndef USER_COMMON_H
#define USER_COMMON_H

#include <string.h>
#include "gkt_types.h"
#include "gkt_board.h"
#include "gkt_malloc.h"
#include "gkt_debug.h"
#include "gkt_flash_partition.h"
#include "gkt_user.h"

#define USER_ALIGNMENT	8
#define USER_ALIGNMASK	(USER_ALIGNMENT - 1)

#define USER_MAGIC	0x55534552
#define USER_FLASH_TRYTIMES	3
#define USER_RECYCLE_SAVE_FAILED_MAXTIMES	6

/* partition header */
struct _user_partition_header {
	uint8_t type;	/* GKT_USER_TYPE_XXX */
	uint8_t	state;
	uint16_t	reserved;
	uint32_t	magic;	/* USER_MAGIC */
} __ALIGNED(USER_ALIGNMENT);
typedef struct _user_partition_header	user_partition_header_s;
#define USER_PARTITION_HEADER_SIZE	sizeof(user_partition_header_s)

#define USER_PARTITION_ST_SAVING	0xff
#define USER_PARTITION_ST_RECYCLE_STARTED	0xf0
#define USER_PARTITION_ST_NORMAL	0x00

#define USER_PARTITION_IS_VALID(partition_header, _type)	\
	((USER_MAGIC == (partition_header)->magic) \
		&& ((uint32_t)(partition_header)->type == (uint32_t)(_type)) \
		&& ((uint32_t)(partition_header)->state != USER_PARTITION_ST_SAVING))

/* user item header */
typedef union _user_authority {
	struct {
	uint8_t	type : 4;	/* user authority
						 * ALSO is used as item saved complete flag
						 *   0xff: the item is saving, not complete
						 *   valid authority value : complete
						 */
	uint8_t threaten : 4;
	};
	uint8_t attr;
} user_authority_u;

typedef union _user_item_header {	/* common header */
	struct {
		uint16_t	user_index;
		user_authority_u authority;
		uint8_t	state;
	};
	uint32_t	attr;
} user_item_header_u;
#define USER_ITEM_HEADER_SIZE	sizeof(user_item_header_u)

typedef union _user_item_extend_header {
	struct {	/* common item extend header */
		uint16_t	user_index;
		user_authority_u authority;
		uint8_t	state;

		uint8_t	reserved;
		uint8_t	update_state;	/* used ONLY when verify updating is enabled */
		uint16_t	seq;		/* used ONLY when verify updating is enabled */
	};
	struct {	/* dynpwd item extend header */
		uint32_t	common_values;

		uint32_t	dynpwd_type : 8;
		uint32_t	dynpwd_seq : 24;
	};
	struct {
		uint32_t	attr;
		uint32_t	extend_attr;
	};
} user_item_extend_header_u;
#define USER_ITEM_EXTEND_HEADER_SIZE	sizeof(user_item_extend_header_u)

#define ISER_ITEM_ST_UNUSED	0xFF
#define USER_ITEM_ST_SAVING	0xFC
#define USER_ITEM_ST_USED	0xF0
#define USER_ITEM_ST_CANCELLED	0xC0
#define USER_ITEM_ST_DELETED	0x00

#define USER_ITEM_UPDATE_ENABLE		0xff
#define USER_ITEM_UPDATE_DISABLE	0x00
#define USER_ITEM_UPDATE_SEQ_INITIAL	0x01

#define USER_ITEM_IS_FREE(item_header)	\
	((uint32_t)~0 == (item_header)->attr)
#define USER_ITEM_EXTEND_IS_FREE(item_extend_header)\
	(((uint32_t)~0 == (item_extend_header)->attr) \
		&& ((uint32_t)~0 == (item_extend_header)->extend_attr))

#define USER_ITEM_IS_STATE(item_header, _state)	\
	(((item_header)->user_index >= GKT_USER_INDEX_MIN) \
		&& ((item_header)->user_index <= GKT_USER_INDEX_MAX) \
		&& ((item_header)->authority.type < GKT_USER_AUTH_TEMPORARY) \
		&& ((item_header)->state == (_state)))
#define USER_ITEM_IS_USED(item_header)	\
	USER_ITEM_IS_STATE(item_header, USER_ITEM_ST_USED)

#define USER_ITEM_IS_INDEX_USED(item_header, _user_index)	\
	(((uint32_t)(item_header)->user_index == (uint32_t)(_user_index)) \
		&& ((item_header)->authority.type < GKT_USER_AUTH_TEMPORARY) \
		&& ((item_header)->state == USER_ITEM_ST_USED))
#define USER_ITEM_IS_AUTH_USED(item_header, _authority)	\
	(((item_header)->user_index >= GKT_USER_INDEX_MIN) \
		&& ((item_header)->user_index <= GKT_USER_INDEX_MAX) \
		&& ((uint32_t)(item_header)->authority.type == (uint32_t)(_authority)) \
		&& ((item_header)->state == USER_ITEM_ST_USED))

#define USER_ITEM_IS_UNUSED(item_header)	\
	(((item_header)->user_index < GKT_USER_INDEX_MIN) \
		|| ((item_header)->user_index > GKT_USER_INDEX_MAX) \
		|| ((item_header)->authority.type > GKT_USER_AUTH_TEMPORARY) \
		|| (((item_header)->state != USER_ITEM_ST_USED) \
			&& ((item_header)->state != USER_ITEM_ST_CANCELLED)))

/* user type operations */
typedef struct _user_type_ops {
	int (*init)(void);
	void	(*config)(gkt_user_cfg_params_s *cfg_params);

	int (*add_item)(uint32_t item_index);
	void (*delete_item)(uint32_t item_index);
	void (*reset_info)(void);
	void (*reset_partition)(void);

	int (*verify)(uint32_t *user_id, uint32_t excluded_user_index,
			uint32_t authority, const uint8_t *data, uint32_t len);
	int (*verify_update)(void);
	void (*verify_finalize)(void);

	int	(*enroll_init)(void);
	int	(*enroll_run)(const uint8_t *data, uint32_t len);
	void	(*enroll_uninit)(void);
} user_type_ops_s;

#define USER_TYPE_OPS_ONE(lc_type_name, func_name)	\
	user_##lc_type_name##_##func_name
#define USER_TYPE_OPS_DEFINE(lc_type_name)	\
	static const user_type_ops_s c_user_##lc_type_name##_ops = {	\
		USER_TYPE_OPS_ONE(lc_type_name, init),	\
		USER_TYPE_OPS_ONE(lc_type_name, config),	\
		USER_TYPE_OPS_ONE(lc_type_name, add_item),	\
		USER_TYPE_OPS_ONE(lc_type_name, delete_item),	\
		USER_TYPE_OPS_ONE(lc_type_name, reset_info),	\
		USER_TYPE_OPS_ONE(lc_type_name, reset_partition),	\
		USER_TYPE_OPS_ONE(lc_type_name, verify),	\
		USER_TYPE_OPS_ONE(lc_type_name, verify_update),	\
		USER_TYPE_OPS_ONE(lc_type_name, verify_finalize),	\
		USER_TYPE_OPS_ONE(lc_type_name, enroll_init),	\
		USER_TYPE_OPS_ONE(lc_type_name, enroll_run),	\
		USER_TYPE_OPS_ONE(lc_type_name, enroll_uninit),	\
	}
#define USER_TYPE_OPS_PTR(lc_type_name)	\
	(&c_user_##lc_type_name##_ops)

enum {
	USER_VERIFY_UPDATE_SUCCESS = 0,
	USER_VERIFY_UPDATE_NEEDLESS,
	USER_VERIFY_UPDATE_DISABLE,
	USER_VERIFY_UPDATE_FAILED,
	USER_VERIFY_UPDATE_MAX
};

/* user info */
typedef struct _user_type_info {
	uint16_t	magic;
	uint16_t	item_free_index;	/* index of top free item */
	uint16_t	auth_user_used_nums[GKT_USER_AUTH_TEMPORARY];
	uint16_t	user_used_nums;
} user_type_info_s;
#define USER_TYPE_INFO_MAGIC	0xEEF3


#define USER_INSTANCE_F_ITEM_HEADER_EXTEND	(1U << 0)
#define USER_INSTANCE_F_ITEM_UPDATE_ENABLE	(1U << 1)
#define USER_INSTANCE_F_ITEM_UPDATE_DATA_ONLY_ENABLE	(1U << 2)
#define USER_INSTANCE_F_ITEM_DATA_SECTOR_ALIGNED	(1U << 8)
#define USER_INSTANCE_F_ITEM_SECTOR_ALIGNED	(1U << 9)
#define USER_INSTANCE_F_NEED_RETRIEVE	(1U << 16)
#define USER_INSTANCE_F_INIT_OK	(1U << 31)

/* user instance */
typedef struct _user_instance {
	uint32_t	type;
	uint32_t	flags;

	const user_type_ops_s	*c_ops;
	const char	*c_partition_name;
	const gkt_flash_partition_info_s	*c_flash_info;
	union {
		const user_partition_header_s	*c_partition_header;
		user_partition_header_s	*v_partition_header;
	};
	union {
		const user_item_header_u	*c_items_base;
		user_item_header_u	*v_items_base;
	};

	user_type_info_s	*info;

	uint32_t	auth_user_maxnums[GKT_USER_AUTH_TEMPORARY];
	uint32_t	user_maxnums;

	uint32_t	item_size;
	uint32_t	item_base_offset;

	uint32_t	item_data_maxsize;		/* split data */
	uint32_t	item_data_base_offset;	/* split data */

	uint32_t	item_available_nums;

	uint32_t	verify_item_index;
	uint32_t	enroll_item_index;

	/* enroll or verify update item header */
	union {
		user_item_header_u	*item_header;
		user_item_extend_header_u	*item_extend_header;
	};
	const void	*item_data;
	uint32_t	item_data_size;
} user_instance_s;

typedef struct _user_items_v {
	uint8_t	*buffer;
	uint32_t	maxsize;
	uint32_t	curr_type;
} user_items_v_s;

#define USER_INDEX_CAPACITY	(GKT_USER_INDEX_MAX - GKT_USER_INDEX_MIN + 1)
#if (USER_INDEX_CAPACITY > GKT_USER_MAXNUMS)
#define __USER_INFO_RETMEM

typedef union _user_index_type_map_item {
	struct {
		uint16_t	user_index;
		uint8_t	type;
		uint8_t	reserverd;
	};
	uint32_t	value;
} user_index_type_map_item_u;

typedef struct _user_index_type_map {
	int	sorted;
	uint32_t	used_nums;
	user_index_type_map_item_u	map_items_table[GKT_USER_MAXNUMS];
} user_index_type_map_s;
#else
#define __USER_INFO_RETMEM	__RETMEM_USR

typedef struct _user_index_type_map {
	uint8_t	index_type_table[GKT_USER_MAXNUMS];
} user_index_type_map_s;
#endif

#define USER_INFO_MAGIC	0x5E1F
typedef struct _user_info {
	uint16_t	magic;
	uint16_t	type_retrieved_bits;
	user_index_type_map_s	index_type_map;	
} user_info_s;

typedef struct _user {
	user_instance_s	*instances[GKT_USER_TYPE_MAX];
	user_items_v_s	items_v;
} user_s;

extern user_s g_user;
#define user_get_instance(type)	g_user.instances[type]

#define USER_ITEM_OFFSET(instance, item_index)	\
	((instance)->item_base_offset + ((item_index) * (instance)->item_size))
#define USER_ITEM_DATA_OFFSET(instance, item_index)	\
	((instance)->item_data_base_offset \
		+ ((item_index) * (instance)->item_data_maxsize))

#define USER_ITEM_HEADER_C(instance, item_index)	\
	((const user_item_header_u *)((uint32_t)(instance)->c_items_base \
		+ ((item_index) * (instance)->item_size)))
#define USER_ITEM_HEADER_V(instance, item_index)	\
	((user_item_header_u *)((uint32_t)(instance)->v_items_base \
		+ ((item_index) * (instance)->item_size)))

#define USER_ITEM_EXTEND_HEADER_C(instance, item_index)	\
	((const user_item_extend_header_u *)((uint32_t)(instance)->c_items_base \
		+ ((item_index) * (instance)->item_size)))
#define USER_ITEM_EXTEND_HEADER_V(instance, item_index)	\
	((user_item_extend_header_u *)((uint32_t)(instance)->v_items_base \
		+ ((item_index) * (instance)->item_size)))

void user_reset_index_map(void);
uint32_t user_get_type(uint32_t user_index);
uint32_t user_get_free_index(uint32_t authority);

const user_item_header_u *user_find_item(user_instance_s *instance,
				uint32_t user_index, uint32_t *item_index);

// agan - 20241017
int __user_reload_items_v(user_instance_s *instance, const char *func, int line);
#define user_reload_items_v(instance)	\
	__user_reload_items_v(instance, __FUNCTION__, __LINE__)

void user_update_item_state(user_instance_s *instance, 
				uint32_t item_index, uint8_t state);
int user_save_item(user_instance_s *instance,
				uint32_t item_index, uint8_t state);

void user_delete_index(user_instance_s *instance,
				uint32_t user_index);
void user_delete_authority(user_instance_s *instance, 
				uint32_t authority);
void user_retrieve(user_instance_s *instance);

int user_recycle(user_instance_s *instance, int stash_restore);

int user_enroll_init(user_instance_s *instance,
				uint32_t authority, uint32_t threaten, uint32_t user_index);
int user_enroll_run(user_instance_s *instance,
				const void *data, uint32_t length);
void user_enroll_uninit(user_instance_s *instance);

int user_verify(user_instance_s *instance, uint32_t *user_id,
				uint32_t excluded_user_index, uint32_t authority, 
				const void *data, uint32_t length);
void user_verify_update(user_instance_s *instance);
void user_verify_finalize(user_instance_s *instance);

void user_reset_common_info(void);
void user_init_common_info(void);

void user_reset_partition(user_instance_s *instance);
void user_reset(user_instance_s *instance);
int user_init(user_instance_s *instance);

#if defined(GKT_CONFIG_USER_TEST_PD_ON_FLASH_WRITE) \
	&& GKT_CONFIG_USER_TEST_PD_ON_FLASH_WRITE
#define user_test_pd_on_flash_write(loc_text)	\
	do {	\
		if (gkt_rand() & 0x01) {	\
			gkt_info("%s: reset ...\n", loc_text);	\
			gkt_system_reset();	\
		}	\
	} while(0)
#else
#define user_test_pd_on_flash_write(loc_text)	do {} while(0)
#endif

#endif	/* USER_COMMON_H */

